import blue
import trinity
import log
import mathUtil
import uicls
import uiutil
import uiconst
import fontConst
import sys
import re

class OldLabelCore(uicls.Sprite):
    __guid__ = 'uicls.OldLabelCore'
    __persistvars__ = (uicls.Sprite.__persistvars__ + ['autowidth',
     'autoheight',
     'busy',
     'textwidth',
     'textheight',
     'text',
     '_defaultcolor',
     '_colorstack',
     '_boundingBoxes',
     '_links',
     '_renderlines',
     '_parsingBuff',
     '_paramsBackup',
     '_tabMargin',
     '_hilite',
     '_currentLink',
     '_activeLink',
     '_numLines',
     '_lastAbs',
     '_inited',
     'tabs',
     'shadow',
     'font',
     'fontsize',
     'uppercase',
     'maxlines',
     'singleline',
     'linespace',
     'letterspace',
     'autoheight',
     'autowidth',
     'underline',
     'bold',
     'italic',
     'mousehilite',
     'allowpartialtext',
     '_measuringText'])
    ALLEFT = 0
    ALRIGHT = 1
    ALCENTER = 2
    default_name = 'label'
    default_state = uiconst.UI_DISABLED
    default_font = fontConst.DEFAULT_FONT
    default_fontsize = fontConst.DEFAULT_FONTSIZE
    default_color = None
    default_text = ''
    default_tabs = []
    default_shadow = [(1, -1, -1090519040)]
    default_uppercase = False
    default_maxlines = None
    default_singleline = 0
    default_linespace = None
    default_letterspace = uiconst.LABELLETTERSPACE
    default_autoheight = 1
    default_autowidth = 1
    default_underline = 0
    default_bold = 0
    default_italic = 0
    default_mousehilite = 0
    default_allowpartialtext = 0
    default_width = 0
    default_height = 0
    default_textalign = ALLEFT
    default_boldlinks = 1
    default_specialIndent = 0

    def _Initialize(self, *args, **kw):
        self.busy = 1
        self.noScale = 1
        self.textwidth = 0
        self.textheight = 0
        self._defaultcolor = mathUtil.LtoI(4294967295L)
        self._colorstack = []
        self._boundingBoxes = []
        self._links = []
        self._renderlines = []
        self._parsingBuff = []
        self._paramsBackup = None
        self._tabMargin = uiconst.LABELTABMARGIN
        self._hilite = 0
        self._currentLink = None
        self._activeLink = None
        self._numLines = None
        self._lastAbs = None
        self._inited = 1
        uicls.Sprite._Initialize(self, *args, **kw)



    def ApplyAttributes(self, attributes):
        uicls.Sprite.ApplyAttributes(self, attributes)
        self.tabs = attributes.get('tabs', self.default_tabs)
        self.shadow = attributes.get('shadow', self.default_shadow)
        self.font = attributes.get('font', self.default_font)
        self.fontsize = attributes.get('fontsize', self.default_fontsize)
        self.uppercase = attributes.get('uppercase', self.default_uppercase)
        self.maxlines = attributes.get('maxlines', self.default_maxlines)
        self.singleline = attributes.get('singleline', self.default_singleline)
        self.linespace = attributes.get('linespace', self.default_linespace)
        self.letterspace = attributes.get('letterspace', self.default_letterspace)
        self.autoheight = attributes.get('autoheight', self.default_autoheight)
        self.autowidth = attributes.get('autowidth', self.default_autowidth)
        self.underline = attributes.get('underline', self.default_underline)
        self.bold = attributes.get('bold', self.default_bold)
        self.italic = attributes.get('italic', self.default_italic)
        self.mousehilite = attributes.get('mousehilite', self.default_mousehilite)
        self.allowpartialtext = attributes.get('allowpartialtext', self.default_allowpartialtext)
        self.boldLinks = attributes.get('boldlinks', self.default_boldlinks)
        self.specialIndent = attributes.get('specialIndent', self.default_specialIndent)
        self._measuringText = attributes.get('measuringText', False)
        self.SetRGB(1.0, 1.0, 1.0, 1.0)
        textcolor = (attributes.get('color', self.default_color) or self.default_color)
        if (textcolor is not None):
            self.SetTextColor(textcolor)
        self.busy = 0
        self.text = attributes.get('text', self.default_text)



    def DoPrepare(self, *args):
        self.texture = None
        self.Layout('DoPrepare')



    def OnSetAttr(self, k, v):
        if self.dead:
            return 
        uicls.Sprite.OnSetAttr(self, k, v)
        if not getattr(self, '_inited', 0):
            return 
        if (k == 'text'):
            if (v is None):
                v = ''
            if not isinstance(v, unicode):
                self.text = unicode(v)
                return 
            self._activeLink = None
            self.Layout(k)
        params = getattr(self, 'params', None)
        if (params and (params.has_key(k) and (params.get(k, None) != v))):
            self.params = params.Copy()
            setattr(self.params, k, v)
            self.Layout(k, params)



    def SetTextColor(self, color):
        tricolor = trinity.TriColor()
        tricolor.SetRGB(*color)
        if (len(color) != 4):
            tricolor.a = 1.0
        self._defaultcolor = tricolor.AsInt()


    SetDefaultColor = SetTextColor

    def SetTabMargin(self, margin, refresh = 1):
        self._tabMargin = margin
        if refresh:
            self.Layout('SetTabMargin')



    def GetTexture(self, flush = 0):
        if ((getattr(self, 'texture', None) is None) or flush):
            self.texture = trinity.TriTexture()
            self.texture.pixels = 'res:/uicore/texture/fill.dds'
        elif not self.texture.pixels:
            self.texture.pixels = 'res:/uicore/texture/fill.dds'
        return self.texture



    def GetRenderer(self):
        if not getattr(self, '_renderer', None):
            self._renderer = uicore.font.GetRenderer()
        return self._renderer



    def GetMeasurer(self):
        if not getattr(self, 'measurer', None):
            self.measurer = uicore.font.GetMeasurer()
        return self.measurer



    def GetParams(self, new = 0):
        if ((not getattr(self, 'params', None)) or new):
            params = uicore.font.GetParams()
            params.font = self.font
            params.fontsize = self.fontsize
            params.underline = self.underline
            params.bold = self.bold
            params.italic = self.italic
            params.letterspace = self.letterspace
            params.linespace = (self.linespace or self.fontsize)
            params.color = self._defaultcolor
            self.params = params
        return self.params



    def SetText(self, text):
        self.text = text



    def GetText(self):
        return self.text



    def GetTab(self, idx, right = None):
        if (len(self.tabs) > idx):
            return self.tabs[idx]
        if (right is not None):
            return right



    def GetLeftOverText(self):
        text = self.text.replace(u'<BR>', u'<br>').replace(u'\r\n', u'<br>').replace(u'<T>', u'<t>')
        if self.singleline:
            text = text.replace(u'<br>', u' ')
        rettext = ''
        characterCount = 0
        currentCharacterAmount = len(self._boundingBoxes)
        startedToAdd = False
        for line in text.split(u'<br>'):
            if startedToAdd:
                rettext += (line + '<br>')
                continue
            for each in line.split(u'>'):
                texttag = each.split(u'<', 1)
                if (len(texttag) == 1):
                    (text, tag,) = (self.Encode(texttag[0]), None)
                else:
                    (text, tag,) = (self.Encode(texttag[0]), texttag[1])
                for word in text.split(u' '):
                    if (characterCount >= currentCharacterAmount):
                        rettext += (word + ' ')
                        startedToAdd = True
                    characterCount += (len(word) + 1)

                characterCount -= 1
                if tag:
                    rettext += (('<' + tag) + '>')

            if startedToAdd:
                rettext += '<br>'

        if startedToAdd:
            rettext = rettext[:-4]
        return rettext



    def Layout(self, hint = 'None', params = None):
        if getattr(self, 'busy', 0):
            return 
        self._boundingBoxes = []
        self.textwidth = 0
        self.textheight = 0
        text = (self.text or u'')
        if not text:
            self.texture = self.GetTexture(1)
            self.GetSurface()
            return 
        if (self.parent is None):
            width = self.width
            height = None
        else:
            (left, top, width, height,) = self.GetAbsolute()
            if (getattr(self, 'autowidth', 0) and (self.align != uiconst.UI_ALCLIENT)):
                width = self.GetMaxWidth()
            elif (self.align == uiconst.UI_ALNONE):
                width = self.width
        self.textalign = self.ALLEFT
        self._currentLink = None
        self._colorstack = []
        self._links = []
        self._renderlines = []
        self._parsingBuff = []
        self._numLines = 0
        p = (params or self.GetParams(1))
        absHeight = 0
        absWidth = 0
        margin = self._tabMargin
        try:
            text = text.replace(u'<BR>', u'<br>').replace(u'\r\n', u'<br>').replace(u'\n', u'<br>').replace(u'<T>', u'<t>')
            if self.singleline:
                text = text.replace(u'<br>', u' ')
            for line in text.split(u'<br>'):
                tabtext = line.split(u'<t>')
                if (len(tabtext) > 1):
                    vScrollshiftX = getattr(self, 'xShift', 0)
                    left = max(0, (margin + vScrollshiftX))
                    for i in xrange(len(tabtext)):
                        self.textalign = self.default_textalign
                        right = (self.GetTab(i, width) - margin)
                        self.Parse(tabtext[i], left, ((right + vScrollshiftX) - left))
                        self.FlushBuff()
                        left = ((right + (margin * 2)) + vScrollshiftX)

                else:
                    self.Parse(tabtext[0], 0, width)
                self.Linebreak()

            if self.lineDiff:
                self.textheight += self.lineDiff
            if self.shadow:
                extendLeft = 0
                extendTop = 0
                extendRight = 0
                extendBottom = 0
                for (sx, sy, scol,) in self.shadow:
                    extendLeft = min(extendLeft, sx)
                    extendRight = max(extendRight, sx)
                    extendTop = max(extendTop, sy)
                    extendBottom = min(extendBottom, sy)

                self.textwidth += (-extendLeft + extendRight)
                self.textheight += (-extendBottom + extendTop)
            if (getattr(self, 'autoheight', 0) and (self.align != uiconst.UI_ALCLIENT)):
                self.height = absHeight = self.textheight
            if (getattr(self, 'autowidth', 0) and (self.align != uiconst.UI_ALCLIENT)):
                self.width = absWidth = self.textwidth

        finally:
            pass

        if (self.parent is None):
            return 
        self._lastAbs = ((absWidth or width), (absHeight or height))
        if (getattr(self, 'autowidth', 0) and (self.align != uiconst.UI_ALCLIENT)):
            self.textwidth = self.textwidth
        else:
            self.textwidth = self.textwidth
        if not self._measuringText:
            self.Render()



    def GetMaxWidth(self, *args):
        return (uicore.desktop.width * 2)



    def Render(self):
        if not hasattr(self, '_renderlines'):
            return 
        surf = self.GetSurface()
        if (surf is None):
            return 
        try:
            k = surf.LockRect(None, 0)
        except:
            self.texture = None
            surf = self.GetSurface()
            if (surf is None):
                return 
            k = surf.LockRect(None, 0)
        (bufferDataData, bufferDataWidth, bufferDataHeight, bufferDataPitch,) = k
        uicore.font.Clear_Buffer(*k)
        renderer = self.GetRenderer()
        autowidth = bool((getattr(self, 'autowidth', 0) and (self.align != uiconst.UI_ALCLIENT)))
        shadowOffsetX = shadowOffsetY = 0
        if self.shadow:
            (sx, sy, scol,) = self.shadow[-1]
            if (sx < 0):
                shadowOffsetX = abs(sx)
            if (sy > 0):
                shadowOffsetY = sy
        if self.shadow:
            extendLeft = 0
            extendTop = 0
            for (sx, sy, scol,) in self.shadow:
                extendLeft = min(extendLeft, sx)
                extendTop = max(extendTop, sy)

            shadowOffsetX = -extendLeft
            shadowOffsetY = extendTop
        for (text, params, shiftX, shiftY, strong, sBits,) in self._renderlines:
            renderer.Reset(k, ((shiftX + shadowOffsetX), ((bufferDataHeight - shiftY) - shadowOffsetY)), self._hilite, self.shadow)
            renderer.AddTextFast(text, params, sBits)

        surf.UnlockRect()



    def Parse(self, line, xoffset, width):
        self.tabwidth = width
        self.tabpos = xoffset
        self.GetMeasurer().Reset(self.tabwidth, self.GetParams())
        for each in line.split(u'>'):
            texttag = each.split(u'<', 1)
            if (len(texttag) == 1):
                (text, tag,) = (self.Encode(texttag[0]), None)
            else:
                (text, tag,) = (self.Encode(texttag[0]), texttag[1])
            if text:
                if self.uppercase:
                    text = text.upper()
                for word in uiutil.FindWordBoundaries(text):
                    if not self.PushWord(word):
                        if tag:
                            self.ParseTag(tag)
                        return 

            if tag:
                self.ParseTag(tag)

        if not self._parsingBuff:
            self.PushWord(u' ')



    def PushWord(self, word, params = None):
        toReturn = True
        if word:
            params = (params or self.GetParams())
            (n, last, bbox, renderData,) = self.GetMeasurer().AddTextGetRenderData(word, params)
            if not n:
                if ((not self.singleline) and self._parsingBuff):
                    if (len(self._parsingBuff) > 1):
                        lastWord = self._parsingBuff[-1][0]
                        lastParams = self._parsingBuff[-1][3]
                        self.Linebreak()
                        if (self.maxlines and (self._numLines >= self.maxlines)):
                            return False
                        else:
                            self.Linebreak()
                        if (self.maxlines and (self._numLines >= self.maxlines)):
                            return False
                        return self.PushWord(word)
                    return False
                if not self.autoheight:
                    measurer = self.GetMeasurer()
                    actualHeight = (measurer.asc - measurer.des)
                    linespace = params.linespace
                    lineheight = (linespace or actualHeight)
                    (_l, _t, _w, height,) = self.GetAbsolute()
                    if ((self.textheight + lineheight) >= height):
                        if self.allowpartialtext:
                            toReturn = False
                        else:
                            return False
                if (n < len(word)):
                    if self.singleline:
                        self._parsingBuff += renderData[:n]
                        for each in renderData[:n]:
                            self._boundingBoxes += each[-1]

                        return False
                    if not self._parsingBuff:
                        self._parsingBuff += renderData[:n]
                        for each in renderData[:n]:
                            self._boundingBoxes += each[-1]

                        return self.PushWord(word[n:])
                    if self._parsingBuff:
                        if (len(self._parsingBuff) > 1):
                            lastWord = self._parsingBuff[-1][0]
                            lastParams = self._parsingBuff[-1][3]
                            if not (lastWord[-1] == ' '):
                                self._parsingBuff = self._parsingBuff[:-1]
                                self.Linebreak()
                                self.PushWord(lastWord, lastParams)
                            else:
                                self.Linebreak()
                        else:
                            self.Linebreak()
                        if (self.maxlines and (self._numLines >= self.maxlines)):
                            return False
                    return self.PushWord(word)
                self._parsingBuff += renderData[:n]
                for each in renderData[:n]:
                    self._boundingBoxes += each[-1]

            return toReturn



    def GetIndexUnderPos(self, pos):
        retIndex = 0
        totalWidth = 0
        if (pos < 0):
            return (retIndex, totalWidth)
        else:
            for sBit in self._boundingBoxes:
                glyphWidth = sBit.xadvance
                if ((totalWidth - (glyphWidth / 2.0)) < pos <= (totalWidth + (glyphWidth / 2.0)) or (totalWidth >= pos)):
                    break
                totalWidth += glyphWidth
                retIndex += 1

            return (retIndex, totalWidth)



    def GetWidthToIndex(self, index):
        if (index == -1):
            index = len(self._boundingBoxes)
        index = min(len(self._boundingBoxes), max(0, index))
        return (index, sum([ each.xadvance for each in self._boundingBoxes[:index] ]))



    def Linebreak(self):
        self.FlushBuff()
        measurer = self.GetMeasurer()
        actualHeight = (measurer.asc - measurer.des)
        linespace = self.GetParams().linespace
        lineheight = (linespace or actualHeight)
        self.lineDiff = None
        if (linespace and (actualHeight > linespace)):
            self.lineDiff = (actualHeight - linespace)
        self.textheight += lineheight
        if self._currentLink:
            self._links[-1][3] = measurer.cursor
            self._links[-1][4] = self.textheight
        measurer.Reset(self.tabwidth)
        measurer.cursor = self.specialIndent
        self._numLines += 1
        if self._currentLink:
            self._links.append([self._currentLink,
             measurer.cursor,
             self.textheight,
             0,
             0])



    def FlushBuff(self):
        if not self._parsingBuff:
            return 
        buffWidth = self._parsingBuff[-1][2]
        pos = self.tabpos
        if (self.textalign == self.ALRIGHT):
            pos += (self.tabwidth - buffWidth)
        elif (self.textalign == self.ALCENTER):
            pos += int(((self.tabwidth - buffWidth) / 2))
        shiftY = (self.textheight + self.GetMeasurer().asc)
        for (text, textStart, textEnd, params, sBits,) in self._parsingBuff:
            self._renderlines.append((text,
             params,
             (pos + textStart),
             shiftY,
             0,
             sBits))
            self.textwidth = max(self.textwidth, ((pos + textStart) + (textEnd - textStart)))

        self._parsingBuff = []



    def ParseTag(self, _tag):
        orgTag = _tag
        tag = _tag.lower()
        params = self.params
        if (tag == u'u'):
            if (self.params.underline != 1):
                self.params = params.Copy()
            self.params.underline = 1
        elif (tag == u'/u'):
            if (self.params.underline != 0):
                self.params = params.Copy()
            self.params.underline = 0
        elif tag.startswith(u'url'):
            self.StartLink(orgTag[4:], params)
        elif (tag == u'/url'):
            self.EndLink(params)
        elif (tag == u'b'):
            if (self.params.bold != 1):
                self.params = params.Copy()
            self.params.bold = 1
        elif (tag == u'/b'):
            if (self.params.bold != 0):
                self.params = params.Copy()
            self.params.bold = 0
        elif (tag == u'i'):
            if (self.params.italic != 1):
                self.params = params.Copy()
            self.params.italic = 1
        elif (tag == u'/i'):
            if (self.params.italic != 0):
                self.params = params.Copy()
            self.params.italic = 0
        elif (tag == u'left'):
            self.textalign = self.ALLEFT
        elif (tag == u'right'):
            self.textalign = self.ALRIGHT
        elif (tag == u'center'):
            self.textalign = self.ALCENTER
        elif tag.startswith(u'fontsize='):
            fs = int(_tag[9:])
            if (self.params.fontsize != fs):
                self.params = params.Copy()
            self.params.fontsize = fs
        elif tag.startswith(u'font='):
            fn = _tag[5:]
            if (self.params.font != fn):
                self.params = params.Copy()
            self.params.font = fn
        elif tag.startswith(u'letterspace='):
            ls = int(_tag[12:])
            if (self.params.letterspace != ls):
                self.params = params.Copy()
            self.params.letterspace = fs
        elif tag.startswith(u'icon='):
            measurer = self.GetMeasurer()
            measurer.AddSpace(16)
            fakeSbit = uiutil.Bunch()
            fakeSbit.xadvance = 16
            self._boundingBoxes.append(fakeSbit)
        elif tag.startswith(u'color='):
            color = uiutil.StringColorToHex(_tag[6:])
            if (color is None):
                color = _tag[6:16]
            col = mathUtil.LtoI(long(color, 0))
            if (self._currentLink and (self._currentLink == self._activeLink)):
                col = -9472
            if (self.params.color != col):
                self.params = params.Copy()
            self.params.color = col
            self._colorstack.append(col)
        elif tag.startswith(u'/color'):
            if (self.params.color != self._defaultcolor):
                self.params = params.Copy()
            if self._colorstack:
                self._colorstack.pop()
            if self._colorstack:
                self.params.color = self._colorstack[-1]
            else:
                self.params.color = self._defaultcolor



    def StartLink(self, url, params):
        url = url.replace('&amp;', '&')
        self._currentLink = url
        self._paramsBackup = params.Copy()
        self.params = params.Copy()
        if (url == self._activeLink):
            self.params.color = -9472
        else:
            self.params.color = (getattr(self, 'linkColor', None) or -23040)
        self.params.bold = self.boldLinks
        measurer = self.GetMeasurer()
        self._links.append([url,
         (self.tabpos + measurer.cursor),
         self.textheight,
         0,
         0])



    def EndLink(self, params):
        if not self._links:
            return 
        measurer = self.GetMeasurer()
        actualHeight = (measurer.asc - measurer.des)
        lineheight = (self.GetParams().linespace or actualHeight)
        self._links[-1][3] = (self.tabpos + measurer.cursor)
        self._links[-1][4] = (self.textheight + lineheight)
        if self._paramsBackup:
            self.params = self._paramsBackup.Copy()
            self._paramsBackup = None
        else:
            self.params = None
        self._currentLink = None



    def GetMenu(self):
        m = []
        if self._activeLink:
            m = uicls.BaseLink().GetLinkMenu(self, self._activeLink.replace('&amp;', '&'))
        m += [(mls.UI_CMD_COPY, self.CopyText)]
        return m



    def CopyText(self):
        blue.pyos.SetClipboardData(self.text)



    def _OnMouseEnter(self, *args):
        if self.mousehilite:
            self._hilite = 1
            self.Render()



    def _OnMouseMove(self, *args):
        if not self._hilite:
            self.CheckLinks()



    def _OnMouseExit(self, *args):
        if self.mousehilite:
            self._hilite = 0
            self.Render()
        else:
            self.CheckLinks()



    def CheckLinks(self):
        aLink = None
        if (self._links and (uicore.uilib.mouseOver == self)):
            mouseX = uicore.uilib.x
            mouseY = uicore.uilib.y
            (left, top, width, height,) = self.GetAbsolute()
            for (url, startX, startY, endX, endY,) in self._links:
                if ((left + startX) < mouseX < (left + endX) and (top + startY) < mouseY < (top + endY)):
                    hint = url
                    aLink = url
                    break

            if not aLink:
                self.sr.hint = None
                self.cursor = uiconst.UICURSOR_DEFAULT
            else:
                self.cursor = uiconst.UICURSOR_SELECT
        if (aLink != self._activeLink):
            self._activeLink = aLink
            self.Layout()



    def _OnClick(self, *args):
        if self._activeLink:
            uicls.BaseLink().ClickLink(self, self._activeLink.replace('&amp;', '&'))



    def Encode(self, text):
        return text.replace(u'&gt;', u'>').replace(u'&lt;', u'<').replace(u'&amp;', u'&').replace(u'&GT;', u'>').replace(u'&LT;', u'<')



    def Decode(self, text):
        return text.replace(u'&', u'&amp;').replace(u'<', u'&lt;').replace(u'>', u'&gt;')



    def _OnResize(self, *args):
        uicls.Sprite._OnResize(self, *args)
        if (self.autowidth and self.autoheight):
            return 
        (left, top, width, height,) = self.GetAbsolute()
        if (getattr(self, 'autowidth', 0) and (self.align != uiconst.UI_ALCLIENT)):
            width = uicore.uilib.desktop.width
        if (self._lastAbs != (width, height)):
            self.Layout('_OnResize')
            if getattr(self, 'OnSizeChanged', None):
                self.OnSizeChanged()



    def GetSurface(self):
        buf = self.GetTexture().pixelBuffer
        if buf:
            textureSize = (buf.width, buf.height)
        else:
            buf = (0, 0)
        surf = None
        if not getattr(self, 'dev', None):
            self.dev = trinity.device
        try:
            if (textureSize != (self.textwidth, self.textheight)):
                (surfaceWidth, surfaceHeight,) = (self.textwidth, self.textheight)
                self.rectWidth = surfaceWidth = max(2, surfaceWidth)
                self.rectHeight = surfaceHeight = max(2, surfaceHeight)
                surf = self.dev.CreateTexture(surfaceWidth, surfaceHeight, 1, 0, trinity.TRIFMT_A8R8G8B8, trinity.TRIPOOL_MANAGED)
                if surf:
                    self.GetTexture().AttachPixels(surf)
                    self.Invalidate()
            else:
                surf = self.GetTexture().pixelBuffer
            return surf
        except Exception, e:
            return 



    def OnSizeChanged(self):
        return 




class LabelCore(OldLabelCore):
    __guid__ = 'uicls.LabelCore'


